using System;
using System.Collections.Generic;
using Microsoft.WowAddonStudio.Lua.RuntimeService.Tests.Common;
using Microsoft.WowAddonStudio.Lua.RuntimeService.Tests.Mocks;
using Microsoft.WowAddonStudio.Lua.RuntimeService.WowBench;
using Microsoft.WowAddonStudio.Lua.RuntimeService.WowBench.Resolvers;
using NUnit.Framework;

namespace Microsoft.WowAddonStudio.Lua.RuntimeService.Tests.WowBench
{
	[TestFixture]
	public class CommandHistoryTest : WoWBenchTestBase
	{
		private IWoWBenchCommandHandler commandHandler;
		private ILuaRuntime runtime;
		private List<string> commands;
		private int invocationCount;

		#region SetUp

		/// <summary>
		/// Tests the fixture set up.
		/// </summary>
		[TestFixtureSetUp]
		public override void TestFixtureSetUp()
		{
			base.TestFixtureSetUp();
			ConfigurationHelper.InitializeServiceLocator(Provider);
			runtime = new LuaRuntime(Provider, new ResolverFactory(Provider), new MockShellHostFactory());
			commandHandler = new WoWBenchController(runtime);
		}

		/// <summary>
		/// Sets up.
		/// </summary>
		[SetUp]
		public override void SetUp()
		{
			base.SetUp();
			commands = new List<string>
			           	{
			           		"print(10);",
			           		"a=10;",
			           		"print(a);",
			           		"print(10 + a);",
			           		"a = nil;"
			           	};
		}

		#endregion

		#region TearDown

		/// <summary>
		/// Tears down.
		/// </summary>
		[TearDown]
		public override void TearDown()
		{
			base.TearDown();
		}

		#endregion

		#region Tests

		/// <summary>
		/// Test_s the simple command navigation prev.
		/// </summary>
		[Test(Description = "")]
		public void Test_SimpleCommandNavigationPrev()
		{
			var commandHistory = new CommandHistory(commandHandler);
			foreach (string command in commands)
			{
				commandHistory.Add(CreateCommand(command));
			}
			Assert.AreEqual(commands.Count + 1, commandHistory.Count);
			Assert.AreEqual(commands[commands.Count - 1], commandHistory.Last.GetCommand());
			Assert.AreEqual(string.Empty, commandHistory.First.GetCommand());

			AssertCommandHistoryElementsPrev(commandHistory, commands);
			Assert.AreEqual(string.Empty, commandHistory.Previous.GetCommand());

			commandHistory.Reset();

			AssertCommandHistoryElementsPrev(commandHistory, commands);
			Assert.AreEqual(string.Empty, commandHistory.Previous.GetCommand());
		}

		/// <summary>
		/// Test_s the simple command navigation next.
		/// </summary>
		[Test(Description = "")]
		public void Test_SimpleCommandNavigationNext()
		{
			var commandHistory = new CommandHistory(commandHandler);
			foreach (string command in commands)
			{
				commandHistory.Add(CreateCommand(command));
			}
			Assert.AreEqual(commands.Count + 1, commandHistory.Count);
			Assert.AreEqual(commands[commands.Count - 1], commandHistory.Last.GetCommand());
			Assert.AreEqual(string.Empty, commandHistory.First.GetCommand());

			AssertCommandHistoryElementsNext(commandHistory, commands);
			Assert.AreEqual(string.Empty, commandHistory.Next.GetCommand());

			commandHistory.Reset();

			AssertCommandHistoryElementsNext(commandHistory, commands);
			Assert.AreEqual(string.Empty, commandHistory.Next.GetCommand());
		}

		/// <summary>
		/// Test_s the command navigation prev next.
		/// </summary>
		[Test(Description = "")]
		public void Test_CommandNavigationPrevNext()
		{
			var commandHistory = new CommandHistory(commandHandler);
			foreach (string command in commands)
				commandHistory.Add(CreateCommand(command));

			Assert.AreEqual(commands.Count + 1, commandHistory.Count);
			Assert.AreEqual(commands[commands.Count - 1], commandHistory.Last.GetCommand());
			Assert.AreEqual(string.Empty, commandHistory.First.GetCommand());

			Assert.AreEqual(commands[commands.Count - 1], commandHistory.Previous.GetCommand());
			Assert.AreEqual(commands[commands.Count - 2], commandHistory.Previous.GetCommand());
			Assert.AreEqual(commands[commands.Count - 1], commandHistory.Next.GetCommand());
			Assert.AreEqual(string.Empty, commandHistory.Next.GetCommand());
			Assert.AreEqual(commands[commands.Count - 1], commandHistory.Last.GetCommand());
			Assert.AreEqual(string.Empty, commandHistory.Next.GetCommand());
			Assert.AreEqual(commands[commands.Count - 1], commandHistory.Previous.GetCommand());
		}


		/// <summary>
		/// Test_s the command history with capacity10.
		/// </summary>
		[Test(Description = "")]
		public void Test_CommandHistoryWithCapacity10()
		{
			var commandHistory = new CommandHistory(commandHandler, 10);
			Console.WriteLine("CommandHistory Capacity: {0}", commandHistory.Capacity);
			for (int index = 1; index < 15; index++)
				commandHistory.Add(CreateCommand(String.Concat("command(", index.ToString(), ");")));

			PrintHistoryItems(commandHistory);
			var first = commandHistory.First;
			Assert.IsNotNull(first);
			for (int index = 5; index < 15; index++)
			{
				ICommandEntry commandEntry = commandHistory.Next;
				Assert.AreEqual(String.Concat("command(", index.ToString(), ");"), commandEntry.GetCommand());
			}
		}

		/// <summary>
		/// Test_s the simple command removal.
		/// </summary>
		[Test(Description = "")]
		public void Test_SimpleCommandRemoval()
		{
			var commandHistory = new CommandHistory(commandHandler);
			foreach (string command in commands)
			{
				commandHistory.Add(CreateCommand(command));
			}
			Assert.AreEqual(commands.Count + 1, commandHistory.Count);

			PrintHistoryItems(commandHistory);

			commandHistory.Remove(commandHistory.First.Next);

			PrintHistoryItems(commandHistory);
			Assert.AreEqual(commands.Count, commandHistory.Count);

			PrintHistoryItems(commandHistory);

			commandHistory.Remove(commandHistory.First.Next.Next);

			PrintHistoryItems(commandHistory);
			Assert.AreEqual(commands.Count - 1, commandHistory.Count);
		}

		/// <summary>
		/// Test_s the one command.
		/// </summary>
		[Test(Description = "")]
		public void Test_OneCommand()
		{
			var commandHistory = new CommandHistory(commandHandler);
			commandHistory.Add(CreateCommand("OneCommand"));
			commandHistory.Remove(commandHistory.First.Next);
			PrintHistoryItems(commandHistory);
			Assert.AreEqual(1, commandHistory.Count);
			commandHistory.Remove(commandHistory.First);
			Assert.AreEqual(1, commandHistory.Count);
			commandHistory.Clear();
			Assert.AreEqual(1, commandHistory.Count);
		}

		/// <summary>
		/// Test_s the event subscription.
		/// </summary>
		[Test(Description = "")]
		public void Test_EventSubscription()
		{
			var commandHistory = new CommandHistory(commandHandler);
			commandHistory.CommandAdded += TestCommandHistoryCommandAdded;
			invocationCount = 0;
			commandHistory.Add(CreateCommand("1. Command"));
			Assert.AreEqual(1, invocationCount);
			commandHistory.UnsubscribeEvents();
			commandHistory.Add(CreateCommand("2. Command"));
			Assert.AreEqual(1, invocationCount);

			invocationCount = 0;
			commandHistory.CommandAdded += TestCommandHistoryCommandAdded;
			commandHistory.CommandAdded += TestCommandHistoryCommandAdded;
			commandHistory.CommandAdded += TestCommandHistoryCommandAdded;
			commandHistory.Add(CreateCommand("3. Command"));
			Assert.AreEqual(3, invocationCount);
			commandHistory.UnsubscribeEvents();
			commandHistory.Add(CreateCommand("2. Command"));
			Assert.AreEqual(3, invocationCount);
		}

		/// <summary>
		/// Tests the command history command added.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="Microsoft.WowAddonStudio.Lua.Runtime.WoWBench.CommandHistoryEventArgs"/> instance containing the event data.</param>
		private void TestCommandHistoryCommandAdded(object sender, CommandHistoryEventArgs e)
		{
			Assert.IsNotNull(e);
			Assert.IsNotNull(e.Command);
			Console.WriteLine("Command added event: {0}", e.Command);
			invocationCount++;
		}

		#endregion

		#region Private Assert functions

		/// <summary>
		/// Asserts the command history elements prev.
		/// </summary>
		/// <param name="commandHistory">The command history.</param>
		/// <param name="commands">The commands.</param>
		private static void AssertCommandHistoryElementsPrev(INavigable<ICommandEntry> commandHistory,
		                                                     IList<string> commands)
		{
			for (int index = commands.Count - 1; index >= 0; index--)
			{
				string command = commandHistory.Previous.GetCommand();
				Console.WriteLine("Assert[{2}]: {0} - {1}", commands[index], command, index);
				Assert.AreEqual(commands[index], command);
			}
		}

		/// <summary>
		/// Asserts the command history elements next.
		/// </summary>
		/// <param name="commandHistory">The command history.</param>
		/// <param name="commands">The commands.</param>
		private static void AssertCommandHistoryElementsNext(INavigable<ICommandEntry> commandHistory,
		                                                     IList<string> commands)
		{
			for (int index = 0; index < commands.Count; index++)
			{
				string command = commandHistory.Next.GetCommand();
				Console.WriteLine("Assert[{2}]: {0} - {1}", commands[index], command, index);
				Assert.AreEqual(commands[index], command);
			}
		}

		#endregion

		#region Private Helpers

		/// <summary>
		/// Creates the command.
		/// </summary>
		/// <param name="command">The command.</param>
		/// <returns></returns>
		private ICommandEntry CreateCommand(string command)
		{
			return new WoWBenchCommand(commandHandler, command);
		}

		/// <summary>
		/// Prints the history items.
		/// </summary>
		/// <param name="commandHistory">The command history.</param>
		private static void PrintHistoryItems(INavigable<ICommandEntry> commandHistory)
		{
			if (commandHistory == null)
				throw new ArgumentNullException("commandHistory");

			ICommandEntry first = commandHistory.First;
			ICommandEntry commandEntry = commandHistory.First;
			int index = -1;
			do
			{
				Console.WriteLine("{0}. {1}", ++index, commandEntry.GetCommand());
				commandEntry = commandHistory.Next;
			} while (commandEntry != first);
		}

		#endregion
	}
}